package it.polimi.jita.cp.block.pc;

import it.polimi.jita.cp.block.Block;
import it.polimi.jita.cp.block.Headers;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.csv.CSVRecord;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class PenaltyCalculator extends
		Block<PenaltyCalculatorInput, PenaltyOutput> {

	private ExpPenalty expPenalty;
	private AsymptoticPenalty asymptoticPenalty;

	@Override
	protected PenaltyOutput computeOutput() {

		PenaltyCalculatorInput input = getInput();
		if (input.isWithAsymptoticPenalty())
			asymptoticPenalty = new AsymptoticPenalty(input.getX0(),
					input.getX1(), input.getX2());

		expPenalty = new ExpPenalty(input.getX0(), input.getX1());

		List<Double> capacityDemand = input.getCapacityDemand();

		PenaltyOutput output = new PenaltyOutput();

		double systemCapacity = input.getSystemCapacity();
		if (input.isWithAsymptoticPenalty()) {
			for (Double cd : capacityDemand) {

				double gap = cd - systemCapacity;
				double vmsNum = input.getVcpuNum();

				double ca = gap > 0 ? systemCapacity : cd;
				double expP = vmsNum * computeExpPenalty(gap / vmsNum);
				double asymptoticP = vmsNum
						* computeAsymptoticPenalty(gap / vmsNum);
				PenaltyInstant penaltyInstant = new PenaltyInstant(cd, ca,
						expP, asymptoticP);
				output.addPenaltyInstant(penaltyInstant);
			}
		} else {
			for (Double cd : capacityDemand) {

				double gap = cd - systemCapacity;
				double vmsNum = input.getVcpuNum();

				double ca = gap > 0 ? systemCapacity : cd;
				double expP = vmsNum * computeExpPenalty(gap / vmsNum);
				PenaltyInstant penaltyInstant = new PenaltyInstant(cd, ca,
						expP, null);
				output.addPenaltyInstant(penaltyInstant);
			}
		}
		return output;
	}

	private double computeExpPenalty(double gap) {
		if (gap <= 0)
			return 0;

		if (gap <= getInput().getX0())
			return gap;

		return expPenalty.getValue(gap);
	}

	private double computeAsymptoticPenalty(double gap) {
		if (gap <= 0)
			return 0;

		if (gap <= getInput().getX0())
			return gap;
		try {
			return asymptoticPenalty.getValue(gap);
		} catch (Exception e) {
			if (getInput().isAsymptoticExceptionCatched())
				return -1;
			else
				throw e;
		}
	}

	@Override
	public Set<JFreeChart> createCharts() {
		Set<JFreeChart> set = new HashSet<>();
		set.add(getCapacityChart());
		return set;
	}

	@Override
	public Map<List<CSVRecord>, CSVRecord> createCSVFiles() {
		Map<List<CSVRecord>, CSVRecord> map = new HashMap<>();

		CSVRecord header;
		if (getInput().isWithAsymptoticPenalty())
			header = createCSVRecord(true, Headers.ALLOCATED_CAPACITY,
					Headers.CAPACITY_DEMAND, Headers.EXP_PENALTY,
					Headers.ASY_PENALTY);
		else
			header = createCSVRecord(true, Headers.ALLOCATED_CAPACITY,
					Headers.CAPACITY_DEMAND, Headers.EXP_PENALTY);

		List<CSVRecord> records = new ArrayList<>();
		List<PenaltyInstant> instants = getOutput().getPenaltyInstants();

		for (PenaltyInstant penaltyInstant : instants) {
			CSVRecord record = createCSVRecord(false,
					penaltyInstant.getCapacityAllocated(),
					penaltyInstant.getCapacityDemand(),
					penaltyInstant.getExpPenalty(),
					penaltyInstant.getAsymptoticPenalty());
			records.add(record);
		}
		map.put(records, header);
		return map;
	}

	private JFreeChart getCapacityChart() {
		// Create a simple XY chart
		XYSeries demand = new XYSeries(Headers.CAPACITY_DEMAND);
		XYSeries allocated = new XYSeries(Headers.ALLOCATED_CAPACITY);
		List<PenaltyInstant> instants = getOutput().getPenaltyInstants();
		int i = 1;
		for (PenaltyInstant penaltyInstant : instants) {
			demand.add(i, penaltyInstant.getCapacityDemand());
			allocated.add(i, penaltyInstant.getCapacityAllocated());
			i++;
		}
		// Add the series to your data set
		XYSeriesCollection dataset = new XYSeriesCollection();

		dataset.addSeries(allocated);
		dataset.addSeries(demand);
		// Generate the graph
		JFreeChart chart = ChartFactory.createXYAreaChart(
				"Capacity allocation", // Title
				"Time (days)", // x-axis Label
				"Capacity (MHz)", // y-axis Label
				dataset, // Dataset
				PlotOrientation.VERTICAL, // Plot Orientation
				true, // Show Legend
				true, // Use tooltips
				false // Configure chart to generate URLs?
				);
		chart.setBackgroundImageAlpha(0.5f);
		chart.getXYPlot().setBackgroundPaint(new Color(230, 230, 230));
		chart.getXYPlot().setForegroundAlpha(1.0f);
		chart.getXYPlot().getDomainAxis().setRange(1, instants.size());

		chart.getXYPlot().getRenderer()
				.setSeriesPaint(0, Color.decode("#fab900"));
		// chart.getXYPlot().getRenderer()
		// .setSeriesPaint(1, Color.decode("#004586aa"));

		XYItemRenderer renderer = chart.getXYPlot().getRenderer();

		// Set a pattern
		BufferedImage bi = new BufferedImage(3, 3, BufferedImage.TYPE_INT_ARGB);
		Graphics g = bi.getGraphics();
		g.setColor(Color.decode("#004586aa"));
		g.drawLine(0, 0, 3, 3);
		TexturePaint tp = new TexturePaint(bi, new Rectangle(0, 0, 3, 3));
		renderer.setSeriesPaint(1, tp);

		return chart;
	}
}
